﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Infrastructure.Helpers.RSAHelper
{
    public static class RSAEncryptUtil
    {
        /// <summary>
        /// 默认编码字符集
        /// </summary>
        private static string DEFAULT_CHARSET = "UTF-8";

        /// <summary>
        /// 公钥加密（超过 私钥长度 / 8 - 11，分段加密）
        /// </summary>
        /// <param name="content">明文</param>
        /// <param name="charset">编码</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static string Encrypt(string content, string charset = "")
        {
            //默认编码格式
            if (string.IsNullOrEmpty(charset))
            {
                charset = DEFAULT_CHARSET;
            }

            //公钥
            string publicKeyPem = RSAKey.PublicKey;
            try
            {
                //假设私钥长度为1024， 1024/8-11=117。
                //如果明文的长度小于117，直接全加密，然后转base64。(data.Length <= maxBlockSize)
                //如果明文长度大于117，则每117分一段加密，写入到另一个Stream中，最后转base64。while (blockSize > 0)
                RSACryptoServiceProvider rsa = RSAUtil.LoadPublicKey(publicKeyPem);
                if (string.IsNullOrEmpty(charset))
                {
                    charset = DEFAULT_CHARSET;
                }
                byte[] data = Encoding.GetEncoding(charset).GetBytes(content);
                int maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制
                if (data.Length <= maxBlockSize)
                {
                    byte[] cipherbytes = rsa.Encrypt(data, false);
                    return Convert.ToBase64String(cipherbytes);
                }
                MemoryStream plaiStream = new MemoryStream(data);
                MemoryStream crypStream = new MemoryStream();
                byte[] buffer = new byte[maxBlockSize];
                int blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
                while (blockSize > 0)
                {
                    byte[] toEncrypt = new byte[blockSize];
                    Array.Copy(buffer, 0, toEncrypt, 0, blockSize);
                    byte[] cryptograph = rsa.Encrypt(toEncrypt, false);
                    crypStream.Write(cryptograph, 0, cryptograph.Length);
                    blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
                }

                return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None);
            }
            catch (Exception ex)
            {
                throw new Exception("EncryptContent = " + content + ",charset = " + charset, ex);
            }
        }

        /// <summary>
        /// 私钥解密（超过 私钥长度 / 8 - 11，分段加密）
        /// </summary>
        /// <param name="content">密文</param>
        /// <param name="charset">编码</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static string Decrypt(string content, string charset = "")
        {
            //默认编码格式
            if (string.IsNullOrEmpty(charset))
            {
                charset = DEFAULT_CHARSET;
            }

            //私钥
            string privateKeyPem = RSAKey.PrivateKey;

            //加密类型
            string keyFormat = RSAKey.KeyType;

            try
            {
                //假设私钥长度为1024， 1024/8 =128。
                //如果明文的长度小于 128，直接全解密。(data.Length <= maxBlockSize)
                //如果明文长度大于 128，则每 128 分一段解密，写入到另一个Stream中，最后 GetString。while (blockSize > 0)

                RSACryptoServiceProvider rsaCsp = RSAUtil.LoadPrivateKey(privateKeyPem, keyFormat);

                //RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPem, signType);
                if (string.IsNullOrEmpty(charset))
                {
                    charset = DEFAULT_CHARSET;
                }
                byte[] data = Convert.FromBase64String(content);
                int maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制
                if (data.Length <= maxBlockSize)
                {
                    byte[] cipherbytes = rsaCsp.Decrypt(data, false);
                    return Encoding.GetEncoding(charset).GetString(cipherbytes);
                }
                MemoryStream crypStream = new MemoryStream(data);
                MemoryStream plaiStream = new MemoryStream();
                byte[] buffer = new byte[maxBlockSize];
                int blockSize = crypStream.Read(buffer, 0, maxBlockSize);
                while (blockSize > 0)
                {
                    byte[] toDecrypt = new byte[blockSize];
                    Array.Copy(buffer, 0, toDecrypt, 0, blockSize);
                    byte[] cryptograph = rsaCsp.Decrypt(toDecrypt, false);
                    plaiStream.Write(cryptograph, 0, cryptograph.Length);
                    blockSize = crypStream.Read(buffer, 0, maxBlockSize);
                }

                return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray());
            }
            catch (Exception ex)
            {
                return null;
            }
        }

    }
}